/**
 * @file
 *           FSOperations.h 
 * @brief
 *          
 *
 * @par Author (last changes):
 *          - Alexandru Chica
 *          
 * @par Description:
 *	Class that deals with file system operations (both logical and phisycal)
 *
 * @par Module-History:
 * @verbatim
 *  Date        Author				Reason
 *  16.01.2010  Alexandru Chica     Creation
 *  17.01.2010  Alexandru Chica     Added copy operation
 *  19.01.2010  Alexandru Chica     Added implementation for delete, version, list directory, get free space...
 *  20.01.2010  Alexandru Chica     Made class singleton
 *
 * @endverbatim
 **/

#include "FSOperations.h"
#include <algorithm>
#include <stdio.h>
#include <shlobj.h>

#define LOGICAL_FS "LogicalFS"
#define TOTAL_FS_SIZE 9999999
#define MAX_FILE_LINE_SIZE 4096

FSOperations* FSOperations::m_pInstance = NULL;

/*
 *  Method: FSOperations::FSOperations()
 *   Descr: 
 */
FSOperations::FSOperations(void) :
  m_pFile(0), m_occupiedFSSize(0)
{
  ZeroMemory(m_pathPrefix, MAX_PATH);

  GetCurrentDirectory(MAX_PATH, m_pathPrefix);

  this->initializeLogicalFile(m_pathPrefix);
}

FSOperations::~FSOperations(void)
{

}

void
FSOperations::cleanUp()
{
  //write data to file
  serializeMemoryMap();

  //uninitialize internal file list
  FileDataContainer_t::iterator it = m_FSContent.begin();
  FileDataContainer_t::iterator endit = m_FSContent.end();
  while (it != endit)
    {
      delete[] it->first;
      ++it;
    }
  m_FSContent.clear();
}

/*
 *  Method: FSOperations::createFile()
 *   Descr: 
 */
bool
FSOperations::createFile(char *szPath, ftype_e type, DWORD size)
{
  bool retVal = true;

  std::string absolutePath;
  std::string relativePath;

  switch (type)
  {
  case IS_FILE:
	  {
		  buildAbsolutePath(m_pathPrefix, szPath, absolutePath);

			//create path to file (dir structure)
		  size_t endOfDirPath = absolutePath.find_last_of("\\");
		  std::string dirPath = absolutePath.substr(0, endOfDirPath); //remove trailing "\"
		  int iRetVal = SHCreateDirectoryEx(NULL, dirPath.c_str(), NULL);

		  if( (iRetVal != ERROR_SUCCESS) && ((iRetVal != ERROR_ALREADY_EXISTS) && (iRetVal != ERROR_FILE_EXISTS) ) )
		  {
			  //failed to create
			  printf(
				  "FSOperations::createFile : failed to created directory %s",
				  absolutePath.c_str());
			  retVal = false;
		  }
		  else
		  {
			  FileData_t fileData;
			  if(strcmp(const_cast<char*>( dirPath.c_str() ), m_pathPrefix) != 0 )
			  {
				  //insert directory in memory map, in case it was created now
				  checkAddDirectoriesToMemoryMap(dirPath);

			  }

			  //TO DO: use CreateFile
			  FILE* f = 0;
			  f = fopen(absolutePath.c_str(), "w");
			  if (f != 0)
			  {
				  fclose(f);

				  //insert created folder into the memory map
				  fileData.fileType = IS_FILE;
				  fileData.dwTimeAccessed = GetTickCount();
				  fileData.dwTimeCreated = GetTickCount();
				  fileData.dwTimeModified = GetTickCount();
				  insertIntoMemoryMap(const_cast<char*> (absolutePath.c_str()),
					  fileData);
			  }
			  else
			  {
				  retVal = false;
			  }
		  }
		  break;
	  }
  case IS_FOLDER:
	  {
		  buildAbsolutePath(m_pathPrefix, szPath, absolutePath);
		  int iRetVal = SHCreateDirectoryEx(NULL, absolutePath.c_str(), NULL);
		  if( iRetVal != ERROR_SUCCESS )
		  {
			  //failed to create
			  printf(
				  "FSOperations::createFile : failed to created directory %s",
				  absolutePath.c_str());
			  retVal = false;
			  break;
		  }
		  else
		  {
			  if(strcmp(const_cast<char*>( absolutePath.c_str() ), m_pathPrefix) != 0 )
			  {
				  //insert directory in memory map, in case it was created now
				  checkAddDirectoriesToMemoryMap(absolutePath);
			  }

			  //insert created folder into the memory map
			  FileData_t fileData;
			  fileData.fileType = IS_FOLDER;
			  insertIntoMemoryMap(const_cast<char*> (absolutePath.c_str()),
				  fileData);
		  }
	  }
	  break;
	  default:
	  {
		  break;
	  }
  }
  return retVal;
}

/*
 *  Method: FSOperations::modifyPath()
 *   Descr: 
 */
bool
FSOperations::modifyPath(char *szPath, char *szNewPath)
{
  bool retVal = true;

  if ((szPath != 0) && (szNewPath != 0))
    {
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          std::string oldAbsolutePath;
          std::string newAbsolutePath;

          buildAbsolutePath(m_pathPrefix, szPath, oldAbsolutePath);
          buildAbsolutePath(m_pathPrefix, szNewPath, newAbsolutePath);

          retVal = ::MoveFile(oldAbsolutePath.c_str(), newAbsolutePath.c_str())
              != 0 ? true : false;
          if (retVal != true)
            {
              printf(
                  "FSOperations::modifyPath : MoveFile from '%s' to '%s' failed with error: 0x%x \n",
                  oldAbsolutePath.c_str(), newAbsolutePath.c_str(),
                  GetLastError());
            }
          else
            {
              printf(
                  "FSOperations::modifyPath : MoveFile from '%s' to '%s' succeeded \n",
                  oldAbsolutePath.c_str(), newAbsolutePath.c_str());
              //update memory map paths
              updateMemoryMap(szPath, szNewPath);
            }
        }
    }
  else
    {
      printf("FSOperations::modifyPath : path or newPath is null \n");
    }

  return retVal;
}

/*
 *  Method: FSOperations::modifySize()
 *   Descr: 
 */
bool
FSOperations::modifySize(char *szPath, DWORD size)
{
  bool retVal = true;
  if (szPath != 0)
    {
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          it->second.dwSize = size;
        }
      else
        {
          printf("FSOperations::modifySize : path was not found \n");
          retVal = false;
        }
    }
  else
    {
      printf("FSOperations::modifySize : null path  \n");
      retVal = false;
    }
  return retVal;
}

/*
 *  Method: FSOperations::updateTimeCreated()
 *   Descr: 
 */
void
FSOperations::updateTimeCreated(char *szPath)
{
  if (szPath != 0)
    {
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          it->second.dwTimeCreated = GetTickCount();
        }
      else
        {
          printf("FSOperations::updateTimeCreated : path was not found \n");
        }
    }
  else
    {
      printf("FSOperations::updateTimeCreated : null path  \n");
    }
}

/*
 *  Method: FSOperations::updateTimeModified()
 *   Descr: 
 */
void
FSOperations::updateTimeModified(char *szPath)
{
  if (szPath != 0)
    {
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          it->second.dwTimeModified = GetTickCount();
        }
      else
        {
          printf("FSOperations::updateTimeModified : path was not found \n");
        }
    }
  else
    {
      printf("FSOperations::updateTimeModified : null path  \n");
    }
}

/*
 *  Method: FSOperations::updateTimeAccessed()
 *   Descr: 
 */
void
FSOperations::updateTimeAccessed(char *szPath)
{
  if (szPath != 0)
    {
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          it->second.dwTimeAccessed = GetTickCount();
        }
      else
        {
          printf("FSOperations::updateTimeAccessed : path was not found \n");
        }
    }
  else
    {
      printf("FSOperations::updateTimeAccessed : null path  \n");
    }
}

/*
 *  Method: FSOperations::setWriteLock()
 *   Descr: 
 */
bool
FSOperations::setWriteLock(char *szPath, bool bWriteLock)
{
  bool retVal = true;
  if (szPath != 0)
    {
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          it->second.bWriteLock = bWriteLock;
        }
      else
        {
          printf("FSOperations::setWriteLock : path was not found \n");
          retVal = false;
        }
    }
  else
    {
      printf("FSOperations::setWriteLock : null path  \n");
      retVal = false;
    }
  return retVal;
}

/*
 *  Method: FSOperations::getWriteLock()
 *   Descr: 
 */
bool
FSOperations::getWriteLock(char *szPath, bool &bLocked)
{
  bool retVal = true;
  if (szPath != 0)
    {
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          bLocked = it->second.bWriteLock;
        }
      else
        {
          printf("FSOperations::getWriteLock : path was not found \n");
          retVal = false;
        }
    }
  else
    {
      printf("FSOperations::getWriteLock : null path  \n");
      retVal = false;
    }
  return retVal;
}

bool
FSOperations::queryFileData(char* szPath, FileData_t& outFileData)
{
  bool retVal = true;
  if (szPath != 0)
    {
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          outFileData = it->second;
        }
      else
        {
          printf("FSOperations::queryFileData : path was not found \n");
          retVal = false;
        }
    }
  else
    {
      printf("FSOperations::queryFileData : null path  \n");
      retVal = false;
    }
  return retVal;
}

bool
FSOperations::openFile(char* szPath, bool bWrite)
{
  bool retVal = true;

  if (szPath != 0)
    {
      //just updates the access time and sets teh write flag
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          if (bWrite == true)
            {
              if (it->second.bWriteLock == true)
                {
                  //file is locked
                  retVal = false;
                }
              else
                {
                  it->second.bWriteLock = true;
                }
            }
          //default update just time accessed
          it->second.dwTimeAccessed = GetTickCount();
        }
      else
        {
          printf("FSOperations::openFile : path is invalid \n");
          retVal = false;
        }
    }
  else
    {
      printf("FSOperations::openFile : path is null \n");
      retVal = false;
    }

  return retVal;
}

bool
FSOperations::closeFile(char *szPath)
{
  bool retVal = true;

  if (szPath != 0)
    {
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          if (it->second.bWriteLock == true)
            {
              //file was locked; Q: which client should reset the flag?
              it->second.dwTimeModified = GetTickCount();
              it->second.bWriteLock = false;
            }

          //default update just time accessed
          it->second.dwTimeAccessed = GetTickCount();
        }
      else
        {
          printf("FSOperations::closeFile : path is invalid \n");
          retVal = false;
        }
    }
  else
    {
      printf("FSOperations::closeFile : path is null \n");
      retVal = false;
    }

  return retVal;
}

int
FSOperations::readFromFile(char* szPath, char* buf, int dataToRead)
{
  int retVal = 0;

  if (szPath != 0)
    {
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          std::string absolutePath;
          buildAbsolutePath(m_pathPrefix, szPath, absolutePath);

		  FILE* f = fopen(absolutePath.c_str(), "r");
		  if (f != NULL)
		  {
			  //retVal = (int)fread_s(buf, dataToRead * sizeof(char), sizeof(char), dataToRead, f);
			  retVal = (int)fread(buf, sizeof(char), dataToRead, f);
			  printf("FSOperations::readFromFile: read %d elements from file %s \n", retVal, absolutePath.c_str());
		  }

          //default update just time accessed
          it->second.dwTimeAccessed = GetTickCount();
        }
      else
        {
          printf("FSOperations::readFromFile : path is invalid \n");
        }
    }
  else
    {
      printf("FSOperations::readFromFile : path is null \n");
    }

  return retVal;
}

int
FSOperations::readFromFile(char* szPath, char* buf, long offset, int dataToRead)
{
  int retVal = 0;

  if (szPath != 0)
    {
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          std::string absolutePath;
          buildAbsolutePath(m_pathPrefix, szPath, absolutePath);

		  FILE* f = fopen(absolutePath.c_str(), "r");
		  if (f != NULL)
		  {
			  //retVal = (int)fread_s(buf, dataToRead * sizeof(char), sizeof(char), dataToRead, f);
			  retVal = fseek(f, offset, SEEK_CUR); //move file pointer to offset
			  if(retVal == 0)
			  {
				  retVal = (int)fread(buf, sizeof(char), dataToRead, f);
				  printf("FSOperations::readFromFile: read %d elements from file %s \n", retVal, absolutePath.c_str());
			  }
			  else
			  {
				retVal = 0; //read 0 bytes

				 printf("FSOperations::readFromFile: FAILED to seek to offset %d \n", offset);
			  }
		  }

          //default update just time accessed
          it->second.dwTimeAccessed = GetTickCount();
        }
      else
        {
          printf("FSOperations::readFromFile : path is invalid \n");
        }
    }
  else
    {
      printf("FSOperations::readFromFile : path is null \n");
    }

  return retVal;
}


int
FSOperations::writeToFile(char* szPath, char* buf, int dataToWrite)
{
  int retVal = 0;

  if (szPath != 0)
    {
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          std::string absolutePath;
          buildAbsolutePath(m_pathPrefix, szPath, absolutePath);

          FILE* f = fopen(absolutePath.c_str(), "w");
          if (f != NULL)
            {
              retVal = fprintf(f, "%s", buf);
              printf(
                  "FSOperations::writeToFile: written %d bytes to file %s \n",
                  retVal, absolutePath.c_str());

			  //update occupied space
			  it->second.dwSize = retVal*sizeof(char);
			  m_occupiedFSSize += it->second.dwSize;
            }

          it->second.dwTimeAccessed = GetTickCount();
          it->second.dwTimeModified = GetTickCount();
        }
      else
        {
          printf("FSOperations::writeToFile : path is invalid \n");
        }
    }
  else
    {
      printf("FSOperations::writeToFile : path is null \n");
    }

  return retVal;
}

int
FSOperations::writeToFile(char* szPath, char* buf, long offset, int dataToWrite)
{
  int retVal = 0;

  if (szPath != 0)
    {
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          std::string absolutePath;
          buildAbsolutePath(m_pathPrefix, szPath, absolutePath);

          FILE* f = fopen(absolutePath.c_str(), "r+");
          if (f != NULL)
            {
				retVal = fseek(f, offset, SEEK_CUR);
				if(retVal == 0)
				{
					retVal = fprintf(f, "%s", buf);
					printf(
						"FSOperations::writeToFile: written %d bytes to file %s \n",
						retVal, absolutePath.c_str());

					//update occupied space
					it->second.dwSize = retVal*sizeof(char);
					m_occupiedFSSize += it->second.dwSize;
				}
				else
				{
					retVal = 0;//0 bytes written
					printf(
						"FSOperations::writeToFile: fseek failed to move file pointer to offset : %d \n",
						offset);
				}
            }

          it->second.dwTimeAccessed = GetTickCount();
          it->second.dwTimeModified = GetTickCount();
        }
      else
        {
          printf("FSOperations::writeToFile : path is invalid \n");
        }
    }
  else
    {
      printf("FSOperations::writeToFile : path is null \n");
    }

  return retVal;
}

bool
FSOperations::listDirectory(char* szPath, LSResult_t& outDirList,
    bool bRecursive)
{
  bool retVal = true;

  std::string workerStr;
  std::string workerPrevDir;

  if (szPath != 0)
  {
		if(strcmp(szPath, "") == 0)
		{
			//list directories from root
			FileDataContainer_t::iterator it = m_FSContent.begin();
			FileDataContainer_t::iterator endIt = m_FSContent.end();
			while(it != endIt)
			{
				char* tempStr = strstr(it->first, "\\");
				if(tempStr == NULL)
				{
					//root element
					LSElement_t tempLsElement;
					//copy path
					size_t tempStrSize = strlen(it->first) + 1;
					tempLsElement.first = new char[tempStrSize];
					strcpy_s(tempLsElement.first, tempStrSize, it->first);
					tempLsElement.second = it->second.fileType;
					outDirList.push_back(tempLsElement);
				}
				++it;
			}

		}

	  FileDataContainer_t::iterator it = m_FSContent.find(szPath);
	  while (it != m_FSContent.end())
	  {
		  char* tempSubStr = 0;
		  tempSubStr = strstr(it->first, szPath);

		  //behaves as startsWith
		  if ((tempSubStr != 0) && (strncmp(tempSubStr, it->first, strlen(
			  szPath)) == 0) //takes care of imbricated paths
			  )
		  {
			  if (bRecursive == false)
			  {
				  if (!workerPrevDir.empty())
				  {
					  if (!checkDirOnCurrentLevel(workerPrevDir.c_str(),
						  it->first))
					  {
						  //entry is not on first level
						  ++it;
						  continue;
					  }
				  }

				  if ((it->second.fileType == IS_FOLDER) && (strcmp(tempSubStr,
					  szPath) != 0)) //skip actual parent dir
				  {
					  workerPrevDir.clear();
					  workerPrevDir += it->first;
				  }
			  }

			  //if(workerStr.empty())
			  if (outDirList.empty())
			  {
				  if (strcmp(tempSubStr, szPath) != 0) //should remove actual parent dir from the listing
				  {
					  //workerStr += it->first;
					  LSElement_t tempLsElement;
					  //copy path
					  size_t tempStrSize = strlen(it->first) + 1;
					  tempLsElement.first = new char[tempStrSize];
					  strcpy_s(tempLsElement.first, tempStrSize, it->first);
					  tempLsElement.second = it->second.fileType;
					  outDirList.push_back(tempLsElement);
				  }
			  }
			  else
			  {
				  /*workerStr += "|";
				  workerStr += it->first;*/

				  LSElement_t tempLsElement;
				  //copy path
				  size_t tempStrSize = strlen(it->first) + 1;
				  tempLsElement.first = new char[tempStrSize];
				  strcpy_s(tempLsElement.first, tempStrSize, it->first);
				  tempLsElement.second = it->second.fileType;
				  outDirList.push_back(tempLsElement);
			  }

			  ++it;
		  }
		  else
		  {
			  //finished appending consecutive paths
			  break;
		  }
	  }
	  //if(workerStr.empty())
	  if (outDirList.empty())
	  {
		  retVal = false;
		  printf("FSOperations::listDirectory : path is invalid \n");
	  }
	  else
	  {
		  /*sizeofEncList = workerStr.size() + 1;
		  outEncodedListing = new char[sizeofEncList];
		  strcpy_s(outEncodedListing, sizeofEncList, workerStr.c_str());*/
	  }
  }
  else
  {
	  printf("FSOperations::listDirectory : path is null \n");
	  retVal = false;
  }

  return retVal;
}

bool
FSOperations::checkPathExists(char* szPath)
{
  bool retVal = true;

  FileDataContainer_t::iterator it = m_FSContent.find(szPath);
  if (it == m_FSContent.end())
    {
      retVal = false;
    }

  return retVal;
}

void
FSOperations::scanDirectory(char* dirPath)
{
  static int functionCallLevel = 0;

  SetCurrentDirectory(dirPath);

  char localDirPath[MAX_PATH];
  strcpy_s(localDirPath, MAX_PATH, dirPath);

  HANDLE hHandle = 0;
  WIN32_FIND_DATA info;

  hHandle = FindFirstFile("*.*", &info);
  if (hHandle != INVALID_HANDLE_VALUE)
    {
      //printf("%s%s \n", info.cFileName);
      while (FindNextFile(hHandle, &info) != FALSE)
        {
          if (!(strcmp(info.cFileName, ".") == 0
              || strcmp(info.cFileName, "..") == 0))
            {
              FileData_t fileData;
              //printf("%s\\%s \n", dirPath ,info.cFileName);
              char* tempDirPath = new char[MAX_PATH];
              strcpy_s(tempDirPath, MAX_PATH,
                  &dirPath[strlen(m_pathPrefix) + 1]);
              if (functionCallLevel != 0)
                {
                  //just to remove one annoying leading backslash
                  strcat_s(tempDirPath, MAX_PATH, "\\");
                }
              strcat_s(tempDirPath, MAX_PATH, info.cFileName);

              printf("%s \n", tempDirPath);

              //calculate additional parameters (file size, total file size in fs...)
              fileData.dwSize = info.nFileSizeHigh * MAXDWORD + 1
                  + info.nFileSizeLow; //TO DO: calculate only for files
              fileData.bWriteLock = false; //not locked by default
              fileData.fileType = info.dwFileAttributes
                  & FILE_ATTRIBUTE_DIRECTORY ? IS_FOLDER : IS_FILE;

              //consider all times as starting from a getTickCount call (time from system boot up)
              fileData.dwTimeAccessed = GetTickCount();
              fileData.dwTimeCreated = GetTickCount();
              fileData.dwTimeModified = GetTickCount();

              //add data to memory map
              m_FSContent.insert(FileDataContainer_t::value_type(tempDirPath,
                  fileData));

              //update occupied space
              m_occupiedFSSize += fileData.dwSize;

              if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                {
                  char oldDirPath[MAX_PATH];
                  strcpy_s(oldDirPath, MAX_PATH, localDirPath);

                  strcat_s(localDirPath, MAX_PATH, "\\");
                  strcat_s(localDirPath, MAX_PATH, info.cFileName);
                  //call scan dir
                  functionCallLevel++;
                  scanDirectory(localDirPath);
                  functionCallLevel--;

                  strcpy_s(localDirPath, MAX_PATH, oldDirPath);
                  //set current dir to old dir
                  SetCurrentDirectory(oldDirPath);
                }
            }
        }
      FindClose(hHandle);
    }
}

void
FSOperations::initializeLogicalFile(char* dirPath)
{
  printf("root is: %s \n", dirPath);
  m_FSContent.clear();

  //read data into memory map
  scanDirectory(dirPath);
}

void
FSOperations::serializeMemoryMap()
{
  //reset the file
  m_pFile = fopen(LOGICAL_FS, "w");
  if (m_pFile != NULL)
    {
      fprintf(m_pFile, "%d|%d\n", TOTAL_FS_SIZE, m_occupiedFSSize);

      //add data from memory to file (after calculating total size)
      char encodedFileLine[MAX_FILE_LINE_SIZE];
      ZeroMemory(encodedFileLine, MAX_FILE_LINE_SIZE);

      FileDataContainer_t::iterator it = m_FSContent.begin();
      FileDataContainer_t::iterator endit = m_FSContent.end();

      while (it != endit)
        {
          ZeroMemory(encodedFileLine, MAX_FILE_LINE_SIZE);

          //encoding: path|file/dir|size|time_created|time_modified|timeaccess|write lock
          sprintf(encodedFileLine, "%s|%s|%d|%d|%d|%d|%d", it->first,
              it->second.fileType == IS_FILE ? "IS_FILE" : "IS_DIRECTORY",
              it->second.dwSize, it->second.dwTimeCreated,
              it->second.dwTimeModified, it->second.dwTimeAccessed,
              it->second.bWriteLock);

          //TO DO: remove temporary var
          fprintf(m_pFile, "%s \n", encodedFileLine);

          ++it;

        }

      fclose( m_pFile);
    }
}

void
FSOperations::buildAbsolutePath(const char* pathPrefix, const char* pathSuffix,
    std::string& absolutePath)
{
  absolutePath.clear();
  absolutePath += pathPrefix;

  if (strcmp(&absolutePath.at(absolutePath.size() - 1), "\\") != 0)
    {
      //prefix does not have a \\
		
      absolutePath += "\\";
    }
  absolutePath += pathSuffix;
}

void
FSOperations::insertIntoMemoryMap(char* szPath, FileData_t& fileData)
{
  char* tempDirPath = new char[MAX_PATH];
  char dirPath[MAX_PATH];
  strcpy_s(dirPath, MAX_PATH, szPath);
  strcpy_s(tempDirPath, MAX_PATH, &dirPath[strlen(m_pathPrefix) + 1]);
  fileData.bWriteLock = 0;
  fileData.dwSize = 0;
  fileData.dwTimeAccessed = GetTickCount();
  fileData.dwTimeCreated = GetTickCount();
  fileData.dwTimeModified = GetTickCount();
  m_FSContent.insert(FileDataContainer_t::value_type(tempDirPath, fileData));
}

void
FSOperations::updateMemoryMap(char* szOldPath, char* szNewPath)
{
  //keys should be ordered alphabetically because of str comparer
  FileDataContainer_t::iterator it = m_FSContent.find(szOldPath);
  while (it != m_FSContent.end())
    {
      char* tempSubStr = 0;
      tempSubStr = strstr(it->first, szOldPath);
      //takes care of imbricated paths
      //behaves as startsWith
      if (tempSubStr != 0 && (strncmp(tempSubStr, it->first, strlen(szOldPath))
          == 0))
        {
          std::string workerStr(it->first);
          std::string workerNewStr(szNewPath);
          workerStr.replace(0, strlen(szOldPath), workerNewStr);

          strcpy_s(it->first, MAX_PATH, workerStr.c_str());
          it->second.dwTimeAccessed = GetTickCount();
          it->second.dwTimeModified = GetTickCount();

          ++it;
        }
      else
        {
          //finished updating consecutive paths
          break;
        }
    }
}

bool
FSOperations::checkDirOnCurrentLevel(const char* prevDir,
    const char* currentDir)
{
  bool retVal = true;

  const char* tempSubStr = 0;
  tempSubStr = strstr(currentDir, prevDir);

  if (tempSubStr != 0 && //prevDir is substring of currentDir
      (strncmp(tempSubStr, currentDir, strlen(prevDir)) == 0) && //currentDir startsWith prevDir
      (strcmp(tempSubStr, prevDir) != 0) //currentDir is not prevDir
  )
    {
      //directories are not on same level (should not appear in non-recursive listing)
      retVal = false;
    }

  return retVal;
}

bool
FSOperations::copyFile(char* szPath, char* szNewPath, bool bOverwrite)
{
  bool retVal = true;

  if (szPath != 0)
    {
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          switch (it->second.fileType)
            {
          case IS_FILE:
            {
              std::string oldPath;
              std::string newPath;

              buildAbsolutePath(m_pathPrefix, szPath, oldPath);
              buildAbsolutePath(m_pathPrefix, szNewPath, newPath);

              retVal = CopyFile(oldPath.c_str(), newPath.c_str(), bOverwrite
                  == false ? TRUE : FALSE) != 0 ? true : false;
              if (retVal)
                {
                  //updateMemoryMap(szPath, szNewPath);
                  insertCopyIntoMemoryMap(const_cast<char*> (newPath.c_str()),
                      it->second);
                }
            }
            break;
          case IS_FOLDER:
            {
              std::string workerOldStr(szPath);
              std::string workerNewStr(szNewPath);
              std::string dirToMove;

              size_t pos = workerOldStr.rfind("\\");
              dirToMove = workerOldStr.substr(pos + 1);

              std::string commonNewAbsolutePath;
              std::string commonOldAbsolutePath;
              buildAbsolutePath(m_pathPrefix, szPath, commonOldAbsolutePath);
              buildAbsolutePath(m_pathPrefix, szNewPath, commonNewAbsolutePath);
              commonNewAbsolutePath += "\\";
              commonNewAbsolutePath += dirToMove;
              if (CreateDirectory(commonNewAbsolutePath.c_str(), NULL))
                {
                  insertCopyIntoMemoryMap(
                      const_cast<char*> (commonNewAbsolutePath.c_str()),
                      it->second);

                  ++it;
                  while (it != m_FSContent.end())
                    {
                      const char* tempSubStr = strstr(it->first,
                          workerOldStr.c_str());
                      if (tempSubStr != 0 && //prevDir is substring of currentDir
                          (strncmp(tempSubStr, it->first, workerOldStr.size())
                              == 0) && //currentDir startsWith prevDir
                          (strcmp(tempSubStr, workerOldStr.c_str()) != 0) //currentDir is not prevDir
                      )
                        {
                          std::string tempOldAbsolutePath;
                          std::string
                              tempNewAbsolutePath(commonNewAbsolutePath);
                          buildAbsolutePath(m_pathPrefix, it->first,
                              tempOldAbsolutePath);

                          std::string relativePath =
                              tempOldAbsolutePath.substr(
                                  commonOldAbsolutePath.size());

                          tempNewAbsolutePath += relativePath.c_str();
                          if (it->second.fileType == IS_FILE)
                            {
                              retVal = CopyFile(tempOldAbsolutePath.c_str(),
                                  tempNewAbsolutePath.c_str(), bOverwrite
                                      == false ? TRUE : FALSE) != 0 ? true
                                  : false;
                              if (retVal)
                                {
                                  insertCopyIntoMemoryMap(
                                      const_cast<char*> (tempNewAbsolutePath.c_str()),
                                      it->second);
                                }
                              else
                                {
                                  printf(
                                      "FSOperations::copyFile: failed to copy file '%s' \n",
                                      tempNewAbsolutePath.c_str());
                                }
                            }
                          else
                            {
                              //IS_FOLDER
                              if (CreateDirectory(tempNewAbsolutePath.c_str(),
                                  NULL))
                                {
                                  insertCopyIntoMemoryMap(
                                      const_cast<char*> (tempNewAbsolutePath.c_str()),
                                      it->second);
                                }
                              else
                                {
                                  printf(
                                      "FSOperations::copyFile: failed to create directory '%s' \n",
                                      commonNewAbsolutePath.c_str());
                                }
                            }
                        }
                      else
                        {
                          //done with files from the same dir
                          break;
                        }
                      ++it;
                    }
                }
              else
                {
                  printf(
                      "FSOperations::copyFile: failed to create directory '%s' \n",
                      commonNewAbsolutePath.c_str());
                }
            }
            break;
          default:
            {
            }
            break;
            }
        }
      else
        {
          printf("FSOperations::copyFile: path is invalid \n");
        }
    }
  else
    {
      printf("FSOperations::copyFile: path is null \n");
    }

  return retVal;
}

void
FSOperations::insertCopyIntoMemoryMap(char *szPath, FileData_t& fileData)
{
  char* tempDirPath = new char[MAX_PATH];
  char dirPath[MAX_PATH];
  strcpy_s(dirPath, MAX_PATH, szPath);
  strcpy_s(tempDirPath, MAX_PATH, &dirPath[strlen(m_pathPrefix) + 1]);
  fileData.bWriteLock = 0;
  fileData.dwTimeAccessed = GetTickCount();
  m_FSContent.insert(FileDataContainer_t::value_type(tempDirPath, fileData));
}

bool
FSOperations::deleteFile(char* szPath, bool bRecursive)
{
  bool retVal = true;

  if (szPath != 0)
    {
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          switch (it->second.fileType)
            {
          case IS_FILE:
            {
              //simply delete the file (no recursivity check)
              std::string tempAbsolutePath;
              buildAbsolutePath(m_pathPrefix, szPath, tempAbsolutePath);
              retVal = DeleteFile(tempAbsolutePath.c_str()) != 0 ? true : false;
			  if(retVal)
			  {
				  //update occupied space
				  m_occupiedFSSize -= it->second.dwSize;
				//remove from logical FS
				  delete[] it->first;
				  m_FSContent.erase(it);
			  }
            }
            break;
          case IS_FOLDER:
            {
              if (bRecursive)
                {
                  //delete all folders and subfolders
                  std::vector<char*> tempDeletionVector;
                  tempDeletionVector.push_back(it->first); //add current dir
                  std::string workerOldStr(it->first);
                  ++it;
                  while (it != m_FSContent.end())
                    {
                      const char* tempSubStr = strstr(it->first,
                          workerOldStr.c_str());
                      if (tempSubStr != 0 && //prevDir is substring of currentDir
                          (strncmp(tempSubStr, it->first, workerOldStr.size())
                              == 0) && //currentDir startsWith prevDir
                          (strcmp(tempSubStr, workerOldStr.c_str()) != 0) //currentDir is not prevDir
                      )
                        {
                          tempDeletionVector.push_back(it->first);//add to deletion vector
                          ++it;
                        }
                      else
                        {
                          break; //no more subdirs/files
                        }
                    }

                  std::sort(tempDeletionVector.begin(),
                      tempDeletionVector.end(), FSOperations::sortPredicate);

                  std::vector<char*>::iterator del_it =
                      tempDeletionVector.begin();
                  std::vector<char*>::iterator end_it =
                      tempDeletionVector.end();
                  while (del_it != end_it)
                    {
                      FileDataContainer_t::iterator actual_del_it =
                          m_FSContent.find(*del_it);
                      if (actual_del_it != m_FSContent.end())
                        {
                          if (actual_del_it->second.fileType == IS_FILE)
                            {
                              if (DeleteFile(actual_del_it->first))
                                {
                                  //deletion was successful
                                  delete[] actual_del_it->first; //deallocate string
                                  m_FSContent.erase(actual_del_it); //delete element from map
                                }
                              else
                                {
                                  printf(
                                      "FSOperations::deleteFile: Failed to delete '%s' \n",
                                      actual_del_it->first);
                                  retVal = false;
                                }
                            }
                          else
                            {
                              if (RemoveDirectory(actual_del_it->first))
                                {
                                  //deletion was successful
                                  delete[] actual_del_it->first; //deallocate string
                                  m_FSContent.erase(actual_del_it); //delete element from map
                                }
                              else
                                {
                                  printf(
                                      "FSOperations::deleteFile: Failed to delete '%s' \n",
                                      actual_del_it->first);
                                  retVal = false;
                                }
                            }
                        }

                      ++del_it;
                    }
                }
              else
                {
                  std::string tempAbsolutePath;
                  buildAbsolutePath(m_pathPrefix, szPath, tempAbsolutePath);
                  //call will fail if dir is not empty
                  retVal
                      = RemoveDirectory(tempAbsolutePath.c_str()) != 0 ? true
                          : false;
				  if(retVal)
				  {
					//remove dir from logical FS
					  delete[] it->first;
					  m_FSContent.erase(it);
				  }
                }
            }
          default:
            {
            }
            break;
            }

        }
      else
        {
          printf("FSOperations::deleteFile: path is invalid \n");
          retVal = false;
        }
    }
  else
    {
      printf("FSOperations::deleteFile: path is null \n");
      retVal = false;
    }

  return retVal;
}

bool
FSOperations::retrieveFileData(char* szPath, FileData_t& outFileData,
    void*& outFileContent)
{
  bool retVal = true;

  if ((szPath != 0))
    {
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          if (it->second.fileType == IS_FILE)
            {
              FILE* f = fopen(szPath, "rb");
              if (f != NULL)
                {
                  fseek(f, 0, SEEK_END);
                  long lFileLength = ftell(f);
                  fseek(f, 0, SEEK_SET);
                  outFileContent = new char[lFileLength];
                  fread(outFileContent, sizeof(char), lFileLength, f);
                  fclose(f);
                }
              outFileData = it->second;
            }
          else
            {
              printf(
                  "FSOperations::retrieveFileData: path represents a folder \n");
              retVal = false;
            }
        }
      else
        {
          printf("FSOperations::retrieveFileData: path is invalid \n");
          retVal = false;
        }
    }
  else
    {
      printf(
          "FSOperations::retrieveFileData: path or ppOutFileContent is null \n");
      retVal = false;
    }
  return retVal;
}

bool
FSOperations::incrementVersion(char *szPath)
{
  bool retVal = true;

  if ((szPath != 0))
    {
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          it->second.version++;
        }
      else
        {
          printf("FSOperations::incrementVersion: path  does not exist \n");
          retVal = false;
        }
    }
  else
    {
      printf("FSOperations::incrementVersion: path  is null \n");
      retVal = false;
    }

  return retVal;
}

bool
FSOperations::decrementVersion(char *szPath)
{
  bool retVal = true;

  if ((szPath != 0))
    {
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          if (it->second.version > 0)
            {
              it->second.version--;
            }
        }
      else
        {
          printf("FSOperations::decrementVersion: path  does not exist \n");
          retVal = false;
        }
    }
  else
    {
      printf("FSOperations::decrementVersion: path  is null \n");
      retVal = false;
    }

  return retVal;
}

bool
FSOperations::checkVersion(char *szPath, int &outVersion)
{
  bool retVal = true;

  if ((szPath != 0))
    {
      FileDataContainer_t::iterator it = m_FSContent.find(szPath);
      if (it != m_FSContent.end())
        {
          outVersion = it->second.version;
        }
      else
        {
          printf("FSOperations::checkVersion: path  does not exist \n");
          retVal = false;
        }
    }
  else
    {
      printf("FSOperations::checkVersion: path  is null \n");
      retVal = false;
    }

  return retVal;
}

DWORD
FSOperations::getTotalSpace()
{
  return TOTAL_FS_SIZE;
}

DWORD
FSOperations::getFreeSpace()
{
  return TOTAL_FS_SIZE - m_occupiedFSSize;
}

FSOperations*
FSOperations::getInstance()
{
  if (m_pInstance == NULL)
    {
      m_pInstance = new FSOperations();
    }
  return m_pInstance;
}

void FSOperations::checkAddDirectoriesToMemoryMap(std::string fullDirPath)
{
	char* relativePath;
	getRelativePath(const_cast<char*>(fullDirPath.c_str()), relativePath);

	FileDataContainer_t::iterator it = m_FSContent.find( relativePath );
	FileData_t fileData;
	fileData.fileType = IS_FOLDER;
	delete[] relativePath;
	//start adding only if path is not already in map
	if(it == m_FSContent.end())
	{
		fileData.dwTimeAccessed = GetTickCount();
		fileData.dwTimeCreated = GetTickCount();
		fileData.dwTimeModified = GetTickCount();

		insertIntoMemoryMap(const_cast<char*>( fullDirPath.c_str() ), fileData);
		bool bReachedExistingPath = false;

		std::string workerDirPath(fullDirPath);
		while(!bReachedExistingPath)
		{
			size_t dirEnd = workerDirPath.find_last_of("\\");
			std::string dirPath = workerDirPath.substr(0, dirEnd); //remove trailing "\"
			getRelativePath(const_cast<char*>(dirPath.c_str()), relativePath);

			it = m_FSContent.find( relativePath );
			if( ( it == m_FSContent.end() ) && (strcmp(const_cast<char*>( dirPath.c_str() ), m_pathPrefix ) != 0) )
			{
				insertIntoMemoryMap( const_cast<char*>( dirPath.c_str() ), fileData );
				workerDirPath = dirPath;
			}
			else
			{
				bReachedExistingPath = true;
			}
			delete[] relativePath;
		}

	}

}

void FSOperations::getRelativePath(char* szAbsolutePath, char*& szOutRelativePath)
{
	szOutRelativePath = new char[MAX_PATH];
	char dirPath[MAX_PATH];
	strcpy_s(dirPath, MAX_PATH, szAbsolutePath);
	strcpy_s(szOutRelativePath, MAX_PATH, &dirPath[strlen(m_pathPrefix) + 1]);
}